home *** CD-ROM | disk | FTP | other *** search
- #include "main.h"
- #include "patch.h"
- #include "patch.sound.h"
-
- /*******************************************************
- * *
- * *
- *******************************************************/
-
- extern int isBackground;
-
- static int soundTrapOn = 0, chPlaying = 0;
- static long oldSndPlay;
- static chPtr theChannels = NULL, chTail = NULL;
- static sndHdl theSounds = NULL;
-
- /*******************************************************
- * *
- * killChannels is called when the application quits to kill off any sound that may *
- * still be playing and placate the sound manager. *
- * *
- *******************************************************/
-
- disposeCh( quitNow )
- int quitNow;
- {
- chPtr next;
- sndHdl theSnd = theChannels->theSnd;
-
- if( theChannels->chan ) SndDisposeChannel( theChannels->chan, quitNow );
- if( theSnd && *theSnd ) disposeSnd( theSnd );
- if( chTail == theChannels ) chTail = NULL;
- next = theChannels->next;
- DisposPtr( theChannels );
- theChannels = next;
- --isBackground;
- }
-
- disposeSnd( theSnd )
- sndHdl theSnd;
- {
- Handle sndData;
- sndPtr thePtr;
-
- HLock( theSnd );
- thePtr = *theSnd;
- if( --( thePtr->useCount ) == 0 )
- {
- sndData = thePtr->theData;
- if( sndData && *sndData )
- DisposHandle( sndData );
- if( thePtr->next ) ( **( thePtr->next )).prev = thePtr->prev;
- if( thePtr->prev ) ( **( thePtr->prev )).next = thePtr->next;
- if( theSounds == theSnd ) theSounds = thePtr->next;
- DisposHandle( theSnd );
- }
- else HUnlock( theSnd );
- }
-
- killChannels()
- {
- while( theChannels ) disposeCh( -1 ); /* force to stop */
- chPlaying = 0;
- }
-
- /*******************************************************
- * *
- * This uses the globals chPlaying and theChannels. DisposeCh diposes the first *
- * channel in the queue. StartUpCh returns an error if it can't start it up. *
- * *
- *******************************************************/
-
- finishChannels()
- {
- if( theChannels && theChannels->stage == chStopped )
- chPlaying = 0;
- while( theChannels && theChannels->stage == chStopped )
- disposeCh( 0 );
- if( !chPlaying )
- while( theChannels && startUpCh( theChannels ))
- disposeCh( 0 );
- else if( theChannels->stage == chStarted )
- sendCallBack( theChannels );
- }
-
- /*******************************************************
- * *
- * doCallBack simply sets a flag in the current channel record saying that it is *
- * completed. The address of this is passed as param2, so that globals are not *
- * really needed. Also, as a0 is the only register used, and this is allowed by *
- * both LSC and the Sound Manager to be munged, I go right ahead. *
- * *
- *******************************************************/
-
- pascal doCallBack( chan, cmd )
- SndCommand *cmd;
- SndChannelPtr chan;
- {
- asm {
- move.l cmd,a0
- move.l 4(a0),a0
- move.w #chStopped,(a0)
- }
- }
-
- /*******************************************************
- * *
- * *
- *******************************************************/
-
- int startUpCh( theCh )
- chPtr theCh;
- {
- sndHdl theSnd = theCh->theSnd;
- Handle theData;
-
- if( theSnd && *theSnd )
- {
- theData = ( **theSnd ).theData;
- if( theData && *theData &&
- !SndNewChannel( &( theCh->chan ), 0, NULL, doCallBack ) &&
- !SndPlay( theCh->chan, theData, -1 ))
- {
- chPlaying = -1;
- theCh->stage = chStarted;
- sendCallBack( theCh );
- return( noErr );
- }
- }
- theCh->stage = chStopped;
- return( -1 );
- }
-
- /*******************************************************
- * *
- * *
- *******************************************************/
-
- sendCallBack( theCh )
- chPtr theCh;
- {
- SndCommand theCmd;
- int result;
-
- theCmd.cmd = callBackCmd;
- theCmd.param1 = 0;
- theCmd.param2 = (long)theCh;
- if( SndDoCommand( theCh->chan, &theCmd, -1 ))
- ++theCh->tryCount;
- else theCh->stage = chSentCall;
- }
-
- /*******************************************************
- * *
- * *
- *******************************************************/
-
- insertSnd( theSnd )
- sndHdl theSnd;
- {
- ( **theSnd ).next = theSounds;
- ( **theSnd ).prev = NULL;
- if( theSounds ) ( **theSounds ).prev = theSnd;
- theSounds = theSnd;
- }
-
- sndHdl newSndCopy( theData )
- Handle theData;
- {
- sndHdl theSnd = NULL;
- sndPtr thePtr;
- long type = 0;
- int id = 0, homeFile = -1, found = 0;
- char deadspace[256];
-
- if(( homeFile = HomeResFile( theData )) != -1 )
- {
- GetResInfo( theData, &id, &type, deadspace );
- theSnd = theSounds;
- while( theSnd &&
- ( thePtr = *theSnd ) && (
- thePtr->rsrcFile != homeFile ||
- thePtr->rsrcType != type ||
- thePtr->rsrcID != id ))
- theSnd = thePtr->next;
- }
- if( theSnd ) ++( **theSnd ).useCount;
- else {
- HandToHand( &theData );
- if( MemError() ) return( 0L );
- theSnd = ( sndHdl )NewHandle(( long )sizeof( sndInfo ));
- if( MemError() ) return( 0L );
- thePtr = *theSnd;
- thePtr->rsrcFile = homeFile;
- thePtr->rsrcType = type;
- thePtr->rsrcID = id;
- thePtr->useCount = 1;
- thePtr->theData = theData;
- insertSnd( theSnd );
- }
- return( theSnd );
- }
-
- /*******************************************************
- * *
- * *
- *******************************************************/
-
- insertCh( newCh )
- chPtr newCh;
- {
- if( chTail )
- {
- chTail->next = newCh;
- chTail = newCh;
- }
- else chTail = theChannels = newCh;
- newCh->next = NULL;
- }
-
- chPtr newChannel( theSnd )
- Handle theSnd;
- {
- chPtr newCh;
- sndHdl sndCopy;
-
- if( theSnd && ( sndCopy = newSndCopy( theSnd )))
- {
- newCh = (chPtr)NewPtr(( long )sizeof( chInfo ));
- if( !MemError() )
- {
- ++isBackground;
- insertCh( newCh );
- newCh->chan = NULL; /* open a channel later when we start to play the sound */
- newCh->theSnd = sndCopy;
- newCh->stage = chCreated;
- newCh->tryCount = 0;
- return( newCh );
- }
- }
- return( 0L );
- }
-
- /*******************************************************
- * *
- * I use a trap during SysBeep instead of just replacing it, as it does a bunch of *
- * error checking, takes care of blinking the menubar, and finds and loads the *
- * sound resource that has been chosen with the Control Panel. Rather than, as *
- * with file IO, executing the instruction asyncronously while doing my stuff, I *
- * return immediately, so that response time picks up, and things feel faster. *
- * In order for this to work properly, I have to make sure that the caller does not *
- * de-allocate the sound while it is being played, so I make a copy of the resource. *
- * If the same sound is used more than once, I only copy it once, and keep a use *
- * count. If there is a channel provided, I pass it through. What could be done *
- * instead is chew my cud until all old calls finish, and then: If async, play it as *
- * requested, but follow it with a special callback, so I know when it's done. If *
- * sync, make it async and play it while waiting around for it to finish. Similarly, *
- * I could patch the other sound calls to make sure nothing ever got through. This *
- * seems silly though…. *
- * *
- *******************************************************/
-
- pascal int doSndPlay( chan, sndHdl, async )
- SndChannelPtr chan;
- Handle sndHdl;
- int async;
- {
- int err;
- asm {
- movem.l d0-d7/a1-a5,-(sp)
- move.l CurrentA5,a5
- }
- if( chan ) asm {
- move.l oldSndPlay,a0
- movem.l (sp)+,d0-d7/a1-a5
- unlk a6
- jmp (a0)
- }
- err = newChannel( sndHdl ) ? noErr : resProblem;
- asm {
- movem.l (sp)+,d0-d7/a1-a5
- }
- return( err );
- }
-
- installSndPatch()
- {
- if( !soundTrapOn ) {
- oldSndPlay = NGetTrapAddress( SndPlayTrapNum, ToolTrap );
- NSetTrapAddress( doSndPlay, SndPlayTrapNum, ToolTrap );
- }
- ++soundTrapOn;
- }
-
- removeSndPatch()
- {
- if( soundTrapOn == 1 )
- NSetTrapAddress( oldSndPlay, SndPlayTrapNum, ToolTrap );
- if( soundTrapOn > 0 ) --soundTrapOn;
- }
-
- pascal doErrSound( which )
- int which;
- {
- if( which ) doSysBeep( 5 );
- }
-
- doSysBeep( count )
- int count;
- {
- installSndPatch();
- SysBeep( count );
- removeSndPatch();
- }